package client;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

import util.Crypto;

public class ClientApplication {
	
	public static void main(String[] args) {
		new ClientApplication();
	}

	private String serial;

	public final static String APPLICATION_TITLE 				= "Client Application";
	public final static String SERIAL_BUTTON_SERIALSHOWN_TRUE	= "Hide the Serial";
	public final static String SERIAL_BUTTON_SERIALSHOWN_FALSE 	= "Show the Serial";
	public final static String VERIFY_BUTTON 					= "Verify";
	public final static String PASSWORD_LABEL 					= "Enter password";
	public final static String RESULT_SUCCESS 					= "Succes!";
	public final static String RESULT_FAIL 						= "Failure!";

	public final static long SECONDS_PASSWORD_ALIVE				= util.Timer.MINUTE_HALF;

	private JFrame frame 					= new JFrame(APPLICATION_TITLE);
	private JButton buttonShowSerial 		= new JButton(SERIAL_BUTTON_SERIALSHOWN_FALSE);
	private JButton buttonVerify 			= new JButton(VERIFY_BUTTON);
	private JLabel labelPassword 			= new JLabel(PASSWORD_LABEL);
	private JTextField textFieldPassword 	= new JTextField();
	private JTextField textFieldSerial 		= new JTextField();
	private JTextField textFieldResult 		= new JTextField();
	private JPanel panel = new JPanel();

	public ClientApplication() {
		// Generate serial. Because we wanted to make a quick serial we deceided to just convert the current
		// time to a hex string, which isn't very secure but serves our purpose. When there needs to be a
		// better/complicated/other serial then change it here. The TOTPApplication doesn't care if the format
		// is changed since it will use the same encryption technique to generate a password, as the 
		// ClientApplication does to compare the password that the user input with what's expected.
		serial = Long.toHexString(util.Timer.getCurrentTimeInSeconds());
		
		// Uncomment this to test the serial with only numbers
		//serial = Integer.toString(5000 + (int)(Math.random()*((10000 - 5000) + 1)));
		
		// The main window
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// Layoutmanager setup
		GroupLayout layout = new GroupLayout(panel);
		panel.setLayout(layout);
		layout.setAutoCreateGaps(true);
		layout.setAutoCreateContainerGaps(true);

		layout.setHorizontalGroup(layout.createSequentialGroup()
				.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING, true)
						.addComponent(buttonShowSerial, 200, 200, 200)
						.addComponent(labelPassword, 200, 200, 200)
						.addComponent(buttonVerify, 200, 200, 200)
						).addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING, true)
								.addComponent(textFieldSerial, 200, 200, 200)
								.addComponent(textFieldPassword, 200, 200, 200)
								.addComponent(textFieldResult, 200, 200, 200)
								)
				);
		layout.setVerticalGroup( layout.createSequentialGroup()
				.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE, true)
						.addComponent(buttonShowSerial, 50, 50, 50)
						.addComponent(textFieldSerial, 50, 50, 50)
						).addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE, true)
								.addComponent(labelPassword, 50, 50, 50)
								.addComponent(textFieldPassword, 50, 50, 50)
								).addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE, true)
										.addComponent(buttonVerify, 50, 50, 50)
										.addComponent(textFieldResult, 50, 50, 50)
										)
				);

		// Actionlistener setup
		buttonShowSerial.addActionListener(new ShowSerialAction());
		buttonVerify.addActionListener(new VerifyPassword());

		// Initial setup what components the user can use or not
		textFieldSerial.setEditable(false);
		textFieldResult.setEditable(false);

		// Look and feel
		textFieldSerial.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 22));
		textFieldSerial.setHorizontalAlignment(JTextField.CENTER);
		labelPassword.setHorizontalAlignment(JLabel.CENTER);
		textFieldPassword.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 22));
		textFieldPassword.setHorizontalAlignment(JTextField.CENTER);
		textFieldResult.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 22));
		textFieldResult.setHorizontalAlignment(JTextField.CENTER);

		// Round up last GUI settings
		frame.setContentPane(panel);
		frame.pack();

		// Set frame at center of screen and show
		Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setLocation((dimension.width / 2) - (frame.getSize().width / 2), dimension.height / 2 - frame.getSize().height / 2);
		frame.setVisible(true);
	}

	// The action listener for the show serial button.
	private class ShowSerialAction implements ActionListener {
		boolean serialIsShown;

		public ShowSerialAction() {
			this(false);
		}
		
		public ShowSerialAction(boolean serialIsShown) {
			this.serialIsShown = serialIsShown;
		}

		public void actionPerformed(ActionEvent e) {
			if(serialIsShown) {
				serialIsShown = false;
				buttonShowSerial.setText(ClientApplication.SERIAL_BUTTON_SERIALSHOWN_FALSE);
				textFieldSerial.setText("");
			} else {
				serialIsShown = true;
				buttonShowSerial.setText(ClientApplication.SERIAL_BUTTON_SERIALSHOWN_TRUE);
				textFieldSerial.setText(serial);
			}
		}
	}

	// The action listener for the verify password button.
	private class VerifyPassword implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			// Get the last moment from current time to generate a code.
			long lastMoment = util.Timer.getLastMomentInMinute(SECONDS_PASSWORD_ALIVE);
			String passWord = Crypto.generateTOTP(serial, Long.toHexString(lastMoment));
			
			if(textFieldPassword.getText().equals(passWord)) {
				textFieldResult.setText(RESULT_SUCCESS);
			} else {
				textFieldResult.setText(RESULT_FAIL);
			}
		}
	}
}
